第7章 静的な構造:クラス
1. オブジェクトは主体ではない
オブジェクト技術におけるすべての元になる基本概念はクラスである。
クラスは(部分的であっても良い)実装を伴う抽象データ型である。
クラスは可能なデータ構造の集合を表し、それらのデータ構造はそのクラスのインスタンス(instance)と呼ばれる。
抽象データ型のインスタンスは純粋な数学的要素
クラスのインスタンスはコンピュータのメモリ上に表現されるデータ構造
オブジェクトは単にクラスのインスタンスのこと
クラスはシステムの作成に使われるソフトウェアテキスト
静的な構造
オブジェクトは実行時のみの概念
実行時に作成され操作される
2. ありがちな混乱を避けるために
3. メタクラス
クラスそのものをオブジェクトとして扱うための機構
メタクラスのインスタンスはクラスである
メタクラスの概念を導入している言語ではSmalltalkが有名
メタクラスを導入すると静的型チェックが困難になる
メタクラスの効果は他の手段によっても代替できる
3. クラスの役割
1. モジュールと型
モジュールはソフトウェア分解の単位
ルーチン、パッケージ、etc...
モジュールへの分解はソフトウェアが提供するサービスとは無関係
どんなプログラムも一つのメインプログラムで書くことは可能
型は特定の動的なオブジェクトの静的な記述
ソフトウェアの実行中に処理される様々なデータ要素の静的な記述
データ(値)を抽象したもの
すべての型はソフトウェアの実行に直接影響を与える
2. モジュールと型としてのクラス
クラスという概念の大きな特徴はモジュールと型の概念を包括し、一つの言語的構造に併合したこと
クラスはモジュールであり、型でもある
4. 一様な型体系
オブジェクトの原則
すべてのオブジェクトは何らかのクラスのインスタンスである
オブジェクト指向の重要な特徴の一つは型体系が単純で一様(uniform)であること
整数や実数、論理値、文字などの基本オブジェクトも定義ずみのライブラリクラスのインスタンスとして考えられる。
一様な型体系の利点
多くの特殊ケースを儲けるよりは一つの単純で一律のフレームワークを使う方が良い
基本的な型をADTとして記述すること、すなわちクラスとして記述することは単純で自然なことである
継承や総称性というオブジェクト指向の機構をすべての型に対して自然に適用できる
5. 単純なクラス
1. 特性
code:Point
TYPES
- POINT
FUNCTIONS
- x: POINT → REAL
- y: POINT → REAL
- rho: POINT → REAL
- theta: POINT → REAL
- translate: POINT × REAL × REAL → POINT
- rotate: POINT × REAL → POINT
- scale: POINT × REAL → POINT
AXIOMS
任意のx, y, a, b: REAL, x: POINTについて以下が成り立つ
- x(translate(p1, a, b)) = x(p1) + a
- y(translate(p1, a, b)) = y(p1) + b
2. 属性とルーチン
クラスにおいては抽象データ型の関数は特性となる
クラスのインスタンスに適用可能な操作となる
ある特性は空間によって表現される
ある情報をそのクラスのすべてのインスタンスに関連付けることによって表現される
属性(attribute)と呼ばれる
ある特性は時間によって表現される
そのクラスのすべてのインスタンスに適用される計算を定義することによって表現される
ルーチン(routine)と呼ばれる
ルーチンは結果を返すファンクションと返さないプロシージャに分類できる
3. 統一形式アクセス
p1という点を操作するときには、p1の内部的な表現がデカルト座標か極座標か気にせずに住むようにしたい
属性とファンクションを明示的に分けることは適切か?
その属性がメモリによって実装されているか、計算によって実装されているかにかかわらず、一つの表記でアクセスできる
例:p1.x
4. クラス
POINTクラスの例
code:point
indexing
description: "2次元の点"
class POINT feature
x, y: REAL
-- 横座標と縦座標
rho: REAL is
-- 原点(0, 0)までの距離
do Result := sqrt(x^2 + y ^ 2) end
theta: REAL is
-- 横軸に対する角度
distance(p: POINT): REAL is
-- pまでの距離
do Result := sqrt((x - p.x)^2)
end
6. 基本的な公文規約
3. 関数の結果の表記
予約後Resultを使う
Resultはデフォルト値で初期化される
code:result
non_negative_value(x:REAL): REAL is
-- 正ならばxの値、それ以外なら0
do
if x > 0.0 then
Result := x
end
end
code:translate(a, b: REAL) is
-- 水平方向にa、垂直方向にb移動する
do
x := x + a; y := y + b
end
どの点のxとyにaとbを足すのか?
現在のインスタンスの明示的な参照には予約後Currentを使う
code:distance
distance(p: POINT): REAL is
-- pまでの距離
do
if p /= Current then
Result := sqrt((x - p.x) ^ 2 + (y - p.y) ^ 2)
end
end
7. オブジェクト指向的なスタイル
2. 顧客と供給者
クラスを使う2通りの方法
そのクラスから継承する
そのクラスの顧客(client)になる
Sはクラスとする。a:Sという形式の宣言を含むクラスCはSの顧客(client)であるという。また、SはCの供給者(supplier)であるという。
この定義では、aはCの属性がファンクションかもしれないし、Cのルーチンのローカルなエンティティか引数かもしれない。
先の例では、クラスPOINTはREALの顧客である。
3. 特性呼び出し
オブジェクト指向の基本的メカニズム
p1.translace(4.0, -1.5)
クラスPOINTの特性translateをp1に適用せよ。
特性呼び出しの基本形式
x.f
x.f(u, v, ...)
xは呼び出しのターゲットと呼ばれる。
2つ目の形式ではfは引数のあるルーチンであり、実引数u, vなどは、Cの中のfの宣言の仮引数と、型の個数が一致する必要がある。
ターゲットxに対して特性fを呼び出した効果
(あれば)個々の仮引数を対応する実引数の値に初期化した後、xにアタッチされているオブジェクトに対して特性fを適用する。
4. 単一ターゲットの原則
特性呼び出しと伝統的なプロシージャの違い
translate(p1, 4.0, -1.5)
すべての引数が同等に扱われている
オブジェクト指向形式の特性呼び出しでは、あるオブジェクトをターゲットとして選び、他の引数は補助的な地位に追いやられる
単一ターゲットの原則
オブジェクト指向におけるすべての操作は、その操作の実行時における現在のインスタンスという特定のオブジェクトに対するものである。
5. モジュールと型の識別
クラスはモジュールであると同時に型である
モジュールはソフトウェアの一部を構成する関連する昨日の集まりである。
モジュールが型ならば、そのモジュールの中のすべての操作はその型の特性インスタンスに対するものである。
モジュールと型の宮ようがどのように行われるか
クラスPOINTを一つのモジュールとして見たとき、このクラスによって提供される機能は型として見たときのクラスPOINTのクラスに対して使用できる操作に他ならない。
6. Currentの役割
あるルーチンに対するすべての呼び出しは、特定のターゲットに対するものである。
ルーチンのテキスト上に現れるすべての特性名をその特定のターゲットに対して適用されるものとして扱う
p1.translate(4.0, -1.5)という呼び出しにおけるtranslateの本体の中のすべてのxの記述は「p1のx」を意味する。
特性呼び出しの原則
F1 ルーチン呼び出しの一部として実行する以外の方法で実行される呼び出し要素は絶対にない。
F2 すべての呼び出しにはターゲットが1つある。
Currentとは「現在の呼び出しのターゲット」である
7. 修飾された呼び出しと修飾されない呼び出し
修飾された呼び出し
呼び出しのターゲットが明示的に指定されているもの
x.f
x.f(u, v, ...)
修飾されない呼び出し
code:transform
transform(a, b, factor: REAL) is
-- (a, b)だけ移動し、factoryを率として拡大/縮小する
do
translate(a, b); scale(factor)
end
修飾されないすべての呼び出しはターゲットをCurrentとする修飾された呼び出しとして書き直すことができる
code:Current.translate
do
Current.translate(a, b); Current.scale(factor)
8. 演算子特性
演算子を特性呼び出しと考える
x + aはx.plus(a)のシンタックスシュガー
code:REAL
indexing
description: "実数"
class REAL feature
inflx "+" (other:REAL): REAL is
-- 現在の値とotherの合計
do ... end
inflx "-"(other:REAL): REAL is
-- 現在の値とotherの差
do ... end
prefix "-": REAL is
-- 符号を逆にした現在の値
end
8. 選択的エクスポートと情報隠蔽
特性の顧客による利用を制限する
code: S
class S feature
-- 完全に開示する
feature {A, B}
-- 顧客のアクセスを制限する
feature{NONE{
-- 非公開にする
end
4. 自分自身にエクスポートする
code: T
indexing
note: "このままでは正しくない"
class T feature
x: T
my_routine is do ... print(x.secret) ... end
feature {NONE}
secret: INTEGER
end
secretは非公開なので、x.secretは無効
解決方法:feature{NONE}の代わりにfeature{T}とすれば良い
9. すべてを一つにする
2. ビッグバン
システムの実行
オブジェクト指向ではソフトウェアシステムの実行は次のステップから成り立っている。
その実行のルートオブジェクト(root object)と呼ばれる特定のオブジェクトを作成する。
生成プロシージャ(creation procedure)と呼ばれる特定のプロシージャをそのオブジェクトに適用する
ルートオブジェクトはシステムのルートクラスと呼ばれる特定クラスのインスタンスである。
生成プロシージャはルートクラスに含まれるプロシージャの一つである。
3. システム
システムの組み立てに必要な三つの小音
クラスの集合CS。これはそのシステムのクラスセットと呼ばれる。
CSの中のどのクラスがルートクラス(root class)かということ。
そのルートクラスのどのプロシージャがルート生成プロシージャかということ。
意味のあるシステムを作るためには、ルートクラスが直接あるいは間接に必要とするクラスすべてがCSに含まれていなければならない。
システムクロージャ
そのシステムのクラスセットの中にルートクラスが必要とするすべてのクラスが含まれている場合にのみ、システムは閉じている。
4. メインプログラムではない
メインプログラムとどこが違うの?
伝統的なメインプログラムでは次の無関係な概念を融合指定た
実行をかいしする場所
システムアーキテクしゃの一番上の、あるいは、根本的な要素
処理の開始時点がシステムの重要な要素である必然性は全くない
オペレーティングシステムにおけるブート手続き
システムの最も重要な属性は、それに含まれるクラスの集合とそれらクラスの個々の機能と、クラス間の属性
システムの進化に合わせて簡単に変更変更できなくてはならない
5. システムを組み立てる
組み立てツールにとって必要な情報
ルートクラスの名前
ユニバース(universe)
ルートが必要とするクラスのテキストを含むファイルの集合
これらのクラステキストには記述できない